﻿// UTF8 模板
#include "OiPCH.hpp"
#include "OiMeasureTool.hpp"
#include "Proc/OiFitLine.hpp"
#include "Proc/OiFitSegment.hpp"
#include "Math/OiOptimize.hpp"

#define SCALETHIN 0.9
#define SCALEHIGH 0.95

namespace Oi
{
    namespace PLScannerCore
    {
        namespace
        {
            bool lineIntersection(const QPointF& a, const QPointF& b, const QPointF& p, const QPointF q, QPointF& res)
            {
                const float Eps = float(1e-8);
                float x1 = a.x();
                float y1 = a.y();
                float x2 = b.x();
                float y2 = b.y();
                float x3 = p.x();
                float y3 = p.y();
                float x4 = q.x();
                float y4 = q.y();
                float den = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4));
                if (fabs(den) <= Eps)
                {
                    return false;
                }

                float d0 = (x1 * y2) - (y1 * x2);
                float d1 = (x3 * y4) - (y3 * x4);
                float numx = (d0 * (x3 - x4)) - (d1 * (x1 - x2));
                float numy = (d0 * (y3 - y4)) - (d1 * (y1 - y2));

                float x = numx / den;
                float y = numy / den;

                res.setX(x);
                res.setY(y);

                return true;
            }

            QPainterPath arrowPath(const QPointF& p1, const QPointF& p2, int direct = 0, int endIndex = 0, int arrowSize = 10)
            {
                QPainterPath path;

                QLineF line(p1, p2);

                double angle = ::acos(line.dx() / line.length());
                if (line.dy() >= 0)
                    angle = (M_PI * 2) - angle;

                if (endIndex == 0 || endIndex == 1)
                {
                    if (direct == 0)
                    {
                        QPointF arrowP1 = p1 + QPointF(sin(angle + M_PI / 3) * arrowSize,
                            cos(angle + M_PI / 3) * arrowSize);
                        QPointF arrowP2 = p1 + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
                            cos(angle + M_PI - M_PI / 3) * arrowSize);

                        path.moveTo(p1);
                        path.lineTo(arrowP1);
                        path.lineTo(arrowP2);
                        path.lineTo(p1);
                    }
                    else
                    {
                        QPointF arrowP1 = p1 - QPointF(sin(angle + M_PI / 3) * arrowSize,
                            cos(angle + M_PI / 3) * arrowSize);
                        QPointF arrowP2 = p1 - QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
                            cos(angle + M_PI - M_PI / 3) * arrowSize);
                        QPointF extendP1 = p1 - QPointF(sin(angle) * 2 * arrowSize,
                            cos(angle) * 2 * arrowSize);

                        path.moveTo(p1);
                        path.lineTo(arrowP1);
                        path.lineTo(arrowP2);
                        path.lineTo(p1);
                        path.lineTo(extendP1);
                    }
                }

                if (endIndex == 0 || endIndex == 2)
                {
                    if (direct == 0)
                    {
                        QPointF arrowP1 = p2 - QPointF(sin(angle + M_PI / 3) * arrowSize,
                            cos(angle + M_PI / 3) * arrowSize);
                        QPointF arrowP2 = p2 - QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
                            cos(angle + M_PI - M_PI / 3) * arrowSize);

                        path.moveTo(p2);
                        path.lineTo(arrowP1);
                        path.lineTo(arrowP2);
                        path.lineTo(p2);
                    }
                    else
                    {
                        QPointF arrowP1 = p2 + QPointF(sin(angle + M_PI / 3) * arrowSize,
                            cos(angle + M_PI / 3) * arrowSize);
                        QPointF arrowP2 = p2 + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
                            cos(angle + M_PI - M_PI / 3) * arrowSize);
                        QPointF extendP2 = p2 + QPointF(sin(angle) * 2 * arrowSize,
                            cos(angle) * 2 * arrowSize);

                        path.moveTo(p2);
                        path.lineTo(arrowP1);
                        path.lineTo(arrowP2);
                        path.lineTo(p2);
                        path.lineTo(extendP2);
                    }
                }

                return path;
            }
            
            QPainterPath circlePath(const QPointF& center, float radius, float scaleX, float scaleY)
            {
                QPainterPath path;

                path.addEllipse(QPointF(center.x() * scaleX, center.y() * scaleY), radius, radius);

                return path;
            }

            float normCVPoint(const cv::Point2f& p)
            {
                float x = p.x;
                float y = p.y;
                return std::sqrtf(x * x + y * y);
            }

            float normQPointF(const QPointF& p)
            {
                double x = p.x();
                double y = p.y();
                double v = std::sqrt(x * x + y * y);
                return static_cast<float>(v);
            }

            float normMathPoint(const Math::Point2f& p)
            {
                double x = p.x();
                double y = p.y();
                double v = std::sqrt(x * x + y * y);
                return static_cast<float>(v);
            }

            Math::Point2f QPointFToMathPointF(const QPointF& p)
            {
                return Math::Point2f(p.x(), p.y());
            }
            
            QPointF MathPointFToQPointF(const Math::Point2f& p)
            {
                return QPointF(p.x(), p.y());
            }

            float pointDis(const QPointF& a, const QPointF& b)
            {
                float dx = a.x() - b.x();
                float dy = a.y() - b.y();
                return std::sqrt(dx * dx + dy * dy);
            }

            float pointDis(const Math::Point2f& a, const Math::Point2f& b)
            {
                float dx = a.x() - b.x();
                float dy = a.y() - b.y();
                return std::sqrt(dx * dx + dy * dy);
            }
            
            float pointDis(const QPointF& a, const Math::Point2f& b)
            {
                float dx = a.x() - b.x();
                float dy = a.y() - b.y();
                return std::sqrt(dx * dx + dy * dy);
            }
            
            float pointDis(const Math::Point2f& a, const QPointF& b)
            {
                float dx = a.x() - b.x();
                float dy = a.y() - b.y();
                return std::sqrt(dx * dx + dy * dy);
            }

            float cornerAngle(const cv::Point2f& a, const cv::Point2f& b, const cv::Point2f& c)
            {
                cv::Point2f dir1 = a - b;
                cv::Point2f dir2 = c - b;
                float val = dir1.dot(dir2);
                val /= normCVPoint(dir1) * normCVPoint(dir2);
                return val;
            }

            bool cmpCvPoint(const cv::Point2f& a, const cv::Point2f& b)
            {
                return a.x < b.x;
            }

            bool cmpMathPoint(const Math::Point2f& a, const Math::Point2f& b)
            {
                return a.x() < b.x();
            }

            bool cmpMathPointY(const Math::Point2f& a, const Math::Point2f& b)
            {
                return a.y() > b.y();
            }

            bool cmpQPointY(const QPointF& a, const QPointF& b)
            {
                return a.y() > b.y();
            }

            // 计算点p到直线a,b的距离，OpenCV
            static float distancePointToLineCV(const cv::Point2f& p, const cv::Point2f& a, const cv::Point2f& b)
            {
                float v1 = std::fabs((b.y - a.y) * p.x - (b.x - a.x) * p.y + b.x * a.y - a.x * b.y);
                float v2 = std::sqrtf((b.y - a.y) * (b.y - a.y) + (b.x - a.x) * (b.x - a.x));
                float d = v1 / v2;
                return d;
            }

            static float distancePointToLineMath(const Math::Point2f& p, const Math::Point2f& a, const Math::Point2f& b)
            {
                float v1 = std::fabs((b.y() - a.y()) * p.x() - (b.x() - a.x()) * p.y() + b.x() * a.y() - a.x() * b.y());
                float v2 = std::sqrtf((b.y() - a.y()) * (b.y() - a.y()) + (b.x() - a.x()) * (b.x() - a.x()));
                float d = v1 / v2;
                return d;
            }

            std::vector<Math::Point2f> filterProfile(const std::vector<Math::Point2f>& arr)
            {
                std::vector<cv::Point2f> tempArr;
                for (auto& p : arr)
                {
                    tempArr.push_back(cv::Point2f(p.x(), p.y()));
                }
                tempArr = Math::orderDiscreteProfile(tempArr, 16);
                std::vector<Math::Point2f> resArr;
                for (auto& p : tempArr)
                {
                    resArr.emplace_back(p.x, p.y);
                }
                return resArr;
            }

            QPointF rotateVector(const QPointF& dir, double angle, bool flag)
            {
                double rad = (angle * M_PI) / 180;
                QPointF res;
                if (flag)
                {
                    float x = static_cast<float>(dir.x() * std::cos(rad) + dir.y() * std::sin(rad));
                    float y = static_cast<float>(-dir.x() * std::sin(rad) + dir.y() * std::cos(rad));
                    res = QPointF(x, y);
                }
                else
                {
                    float x = static_cast<float>(dir.x() * std::cos(rad) - dir.y() * std::sin(rad));
                    float y = static_cast<float>(dir.x() * std::sin(rad) + dir.y() * std::cos(rad));
                    res = QPointF(x, y);
                }
                res /= normQPointF(res);
                return res;
            }

            Math::Point2f rotateMathVector(const Math::Point2f& dir, double angle, bool flag)
            {
                double rad = (angle * M_PI) / 180;
                Math::Point2f res;
                if (flag)
                {
                    float x = static_cast<float>(dir.x() * std::cos(rad) + dir.y() * std::sin(rad));
                    float y = static_cast<float>(-dir.x() * std::sin(rad) + dir.y() * std::cos(rad));
                    res = Math::Point2f(x, y);
                }
                else
                {
                    float x = static_cast<float>(dir.x() * std::cos(rad) - dir.y() * std::sin(rad));
                    float y = static_cast<float>(dir.x() * std::sin(rad) + dir.y() * std::cos(rad));
                    res = Math::Point2f(x, y);
                }
                res /= normMathPoint(res);
                return res;
            }

            bool mathPointInLine(const Math::Point2f& a, const Math::Point2f& b, const Math::Point2f& p)
            {
                auto dirAB = b - a;
                auto dirBA = a - b;
                auto dirAP = p - a;
                auto dirBP = p - b;
                float val1 = dirAB.dot(dirAP);
                float val2 = dirBA.dot(dirBP);
                return val1 >= 0 && val2 >= 0;
            }

            Math::Point2f perpendicularFoot(const Math::Point2f& p, const Math::Point2f& a, const Math::Point2f& b)
            {
                if (a == b) {
                    return a;
                }
                auto dotmultiply = (p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y());
                auto relation = dotmultiply / ((b.x() - a.x()) * (b.x() - a.x()) + (b.y() - a.y()) * (b.y() - a.y()));

                return Math::Point2f(a.x() + relation * (b.x() - a.x()), a.y() + relation * (b.y() - a.y()));

            }

            template <typename T>
            T distance(Cv::Point2<T> p, Cv::Point2<T> p1, Cv::Point2<T> p2)
            {
                T x = p1.x;
                T y = p1.y;
                T dx = p2.x - x;
                T dy = p2.y - y;

                T s = (p1.x - p.x) * (p2.y - p.y) - (p1.y - p.y) * (p2.x - p.x);

                if (dx != 0.0f || dy != 0.0f)
                {
                    float t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);

                    if (t > 1)
                    {
                        x = p2.x;
                        y = p2.y;
                    }
                    else if (t > 0)
                    {
                        x += dx * t;
                        y += dy * t;
                    }
                }

                dx = p.x - x;
                dy = p.y - y;

                if (s >= 0)
                {
                    return +(dx * dx + dy * dy);
                }
                else
                {
                    return -(dx * dx + dy * dy);
                }
            }

            /** @brief find profile curviest.
            @param orient       > 0:  获得最突出的位置.
            @                   <= 0: 获得最凹陷的位置.
            @param threshold    初始的距离值.
             */
            template <typename T>
            int simplify(const Base::List<Cv::Point2<T>>& points, int orient = 1, T thresh = T(0))
            {
                if (points.empty())
                {
                    return 0;
                }
                if (points.size() == 1)
                {
                    return 0;
                }
                if (points.size() == 2)
                {
                    return 1;
                }
                if (points.size() == 3)
                {
                    return 1;
                }

                T maxDist = T(0);
                T curDist = T(0);

                if (orient > 0)
                {
                    maxDist = thresh;
                }
                else
                {
                    maxDist = -thresh;
                }

                const Cv::Point2<T>& beg = points.front();
                const Cv::Point2<T>& end = points.back();

                int maxIndex = -1;

                if (orient > 0)
                {
                    for (int i = 1; i < (int)points.size() - 1; i++)
                    {
                        if ((curDist = distance(points[i], beg, end)) >= maxDist)
                        {
                            maxDist = curDist; maxIndex = i;
                        }
                    }
                }
                else
                {
                    for (int i = 1; i < (int)points.size() - 1; i++)
                    {
                        if ((curDist = distance(points[i], beg, end)) <= maxDist)
                        {
                            maxDist = curDist; maxIndex = i;
                        }
                    }
                }

                return maxIndex;
            }
        }

        class MeasureToolImpl :public Impl<MeasureTool>
        {
        public:
            void init(const ProductParams& prdParam);
            void exit();
            std::vector<QPointF> measure(const OiProfile& profile);
            std::vector<QPointF> boundPoints(const OiProfile& profile) const;
            std::pair<QPointF, QPointF> findWidthPoint(Core::KDTreePtr kd, const std::vector<QPointF>& boundArr) const;
            std::pair<QPointF, QPointF> findEndPointWidthPoint(Core::KDTreePtr kd, std::vector<QPointF>& boundArr) const;
            std::pair<QPointF, QPointF> findWaistPoint(Core::KDTreePtr kd, const std::vector<QPointF>& boundArr, float boundWaist) const;
            std::pair<QPointF, QPointF> findWaistPoint2(Core::KDTreePtr kd, const QPointF& nearWaistTop, const QPointF& nearWaistBot) const;
            std::pair<QPointF, QPointF> findMidPoint(
                Core::KDTreePtr kd, const std::vector<QPointF>& boundArr, const QPointF& nearThickTop, const QPointF& nearFullLeft,const QPointF& nearFullRight) const;
            std::pair<QPointF, QPointF> findWaistHeightPoint(
                Core::KDTreePtr kd, const std::vector<QPointF>& boundArr, const QPointF& nearThickBot, QPointF& nearWaistBot) const;
            std::pair<QPointF, QPointF> findFullHightPoint(Core::KDTreePtr kd, const std::vector<QPointF>& boundArr) const;
            QPointF findCornerFromProfile(const std::vector<cv::Point2f>& ptArr, int* index, float* maxDis = nullptr) const;
            std::pair<QPointF, QPointF> findBottomWidthPoint(const OiProfile& profile, const QPointF& nearWidthTop, const QPointF& nearWidthBot) const;
            float calibArea(const OiProfile& profile) const;
            QPointF findInnerCorner(
                Core::KDTreePtr kd, const QPointF& boundRigh,
                float xBeg, float xEnd, float yBeg, float yEnd,
                const Geom::Line2f& midLine,
                const Math::Point2f& midP,
                bool flag
            ) const;
            QPointF findInnerCorner2(
                Core::KDTreePtr kd, const QPointF& boundRigh,
                float xBeg, float xEnd, float yBeg, float yEnd,
                const Geom::Line2f& midLine,
                const Math::Point2f& midP,
                bool flag
            ) const;
            Math::Point2f searchMinDisPoint(const std::vector<Math::Point2f> arr, const Math::Point2f& p1, const Math::Point2f& p2) const;

        public:
            QPointF widthTop;
            QPointF widthBot;
            QPointF endPointWidthTop;
            QPointF endPointWidthBot;
            QPointF waistTop;
            QPointF waistBot;
            QPointF midBot;
            QPointF midTop;
            QPointF waistHighLef;
            QPointF waistHighRig;
            QPointF fullHighLef;
            QPointF fullHighRig;
            QPointF bottomWidthTop;
            QPointF bottomWidthBot;

            QPointF dspWidthTop;
            QPointF dspWidthBot;
            QPointF dspEndPointWidthTop;
            QPointF dspEndPointWidthBot;
            QPointF dspWaistTop;
            QPointF dspWaistBot;
            QPointF dspMidBot;
            QPointF dspMidTop;
            QPointF dspWaistHighLef;
            QPointF dspWaistHighRig;
            QPointF dspFullHighLef;
            QPointF dspFullHighRig;
            QPointF dspBottomWidthTop;
            QPointF dspBottomWidthBot;

            DefinitionType tp = DT_Mutational;

            QVariantMap config;

            float currArea = 0;

        public:
            QPointF _hoverPos;
        };

        void MeasureToolImpl::init(const ProductParams& prdParam)
        {
            OI_Q(MeasureTool);

            config = prdParam.config;

            widthBot = QPointF(0, 0);
            waistBot = widthBot;
            auto& dfs = prdParam.definitionPoints;
            tp = DT_Mutational;
            if (!dfs.isEmpty())
            {
                tp = static_cast<DefinitionType>(dfs[0].type);
            }
            if (prdParam.profiles.isEmpty())
            {
                OiWarning() << "Template profiles is empty";
                return;
            }

            if (prdParam.config.contains("截面积"))
            {
                currArea = prdParam.config["截面积"].toFloat();
            }

            auto& keyPtArr = prdParam.profiles[0];
            switch (tp)
            {
            case DT_TempleStandLine:
            case DT_TempleStandLineMark:
            {
                if (keyPtArr.size() != 10)
                {
                    OiWarning() << "Template A data error: " << keyPtArr.size();
                    return;
                }
                endPointWidthBot = keyPtArr[1];
                endPointWidthTop = keyPtArr[2];
                waistTop = keyPtArr[3];
                widthTop = keyPtArr[4];
                widthBot = keyPtArr[9];
                fullHighLef = keyPtArr[8];
                fullHighRig = keyPtArr[1];
                midBot = QPointF((fullHighLef.x()+fullHighRig.x()) / 2, endPointWidthBot.y());
                Geom::Line2d line(
                    Geom::Point2d(waistTop.x(), waistTop.y()),
                    Geom::Point2d(endPointWidthTop.x(), endPointWidthTop.y())
                );
                midTop = QPointF(midBot.x(), line.y_at_x(midBot.x()));
                waistHighLef = QPointF(0, 0);
                waistHighRig = endPointWidthBot;
                bottomWidthTop = keyPtArr[6];
                bottomWidthBot = keyPtArr[7];


                bool isOk = lineIntersection(widthTop, waistTop, keyPtArr[0], endPointWidthBot, waistBot);
                if (!isOk)
                {
                    waistBot = widthBot;
                }

                break;
            }
            case DT_TempleReveseLie:
            case DT_TempleReveseLieMark:
            {
                if (keyPtArr.size() != 8)
                {
                    OiWarning() << "Template B data error: " << keyPtArr.size();
                    return;
                }
                endPointWidthBot = keyPtArr[1];
                endPointWidthTop = keyPtArr[2];
                waistTop = keyPtArr[3];
                widthTop = keyPtArr[4];
                widthBot = keyPtArr[0];
                waistHighLef = widthBot + QPointF(2, 0);
                waistHighRig = endPointWidthBot;
                fullHighLef = keyPtArr[5];
                fullHighRig = keyPtArr[1];
                midBot = QPointF((fullHighLef.x() + fullHighRig.x()) / 2, endPointWidthBot.y());
                Geom::Line2d line(
                    Geom::Point2d(waistTop.x(), waistTop.y()),
                    Geom::Point2d(endPointWidthTop.x(), endPointWidthTop.y())
                );
                midTop = QPointF(midBot.x(), line.y_at_x(midBot.x()));

                bottomWidthTop = keyPtArr[6];
                bottomWidthBot = keyPtArr[0];

                bool isOk = lineIntersection(widthBot, waistTop, keyPtArr[0], endPointWidthBot, waistBot);
                if (!isOk)
                {
                    waistBot = widthBot;
                }

                break;
            }
            case DT_TempleStandPart: 
            case DT_TempleStandPartMark: 
            {
                if (keyPtArr.size() < 10)
                {
                    OiWarning() << "Template B data error: " << keyPtArr.size();
                    return;
                }
                endPointWidthBot = keyPtArr[keyPtArr.size() - 1];
                endPointWidthTop = keyPtArr[keyPtArr.size() - 2];
                waistTop = keyPtArr[7];
                widthTop = keyPtArr[6];
                widthBot = keyPtArr[1];
                fullHighLef = keyPtArr[2];
                fullHighRig = endPointWidthBot;
                //midBot = QPointF((fullHighLef.x() + fullHighRig.x()) / 2, endPointWidthBot.y());
                //Geom::Line2d line = Geom::Line2d(
                //    Geom::Point2d(waistTop.x(), waistTop.y()),
                //    Geom::Point2d(endPointWidthTop.x(), endPointWidthTop.y())
                //);
                //for (int i = 2; i <= 8; ++i)
                //{
                //    if (midBot.x() > keyPtArr[i].x() && midBot.x() < keyPtArr[i + 1].x())
                //    {
                //        line = Geom::Line2d(
                //                Geom::Point2d(keyPtArr[i].x(), keyPtArr[i].y()),
                //                Geom::Point2d(keyPtArr[i + 1].x(), keyPtArr[i + 1].y())
                //            );
                //        break;
                //    }
                //
                //}
                //midTop = QPointF(midBot.x(), line.y_at_x(midBot.x()));

                waistHighLef = keyPtArr[0];
                waistHighRig = endPointWidthBot;

                bottomWidthTop = keyPtArr[4];
                bottomWidthBot = keyPtArr[3];

                bool isOk = lineIntersection(widthTop, waistTop, keyPtArr[0], endPointWidthBot, waistBot);
                if (!isOk)
                {
                    waistBot = widthBot;
                }

                break;
            }
            case DT_TempleStandArcs: 
            case DT_TempleStandArcsMark: 
            {
                if (keyPtArr.size() != 10)
                {
                    OiWarning() << "Template B data error: " << keyPtArr.size();
                    return;
                }

                endPointWidthBot = keyPtArr[1];
                endPointWidthTop = keyPtArr[2];
                waistTop = keyPtArr[3];
                widthTop = keyPtArr[4];
                widthBot = keyPtArr[9];
                waistHighLef = QPointF(0, 0);
                waistHighRig = endPointWidthBot;
                fullHighLef = keyPtArr[8];
                fullHighRig = keyPtArr[1];
                bool isOk = lineIntersection(widthTop, waistTop, keyPtArr[0], endPointWidthBot, waistBot);
                if (!isOk)
                {
                    waistBot = widthBot;
                }

                bottomWidthTop = keyPtArr[6];
                bottomWidthBot = keyPtArr[7];

                break;
            }
            //case DT_TriBoard: {
            //    if (keyPtArr.size() != 9)
            //    {
            //        OiWarning() << "Template B data error: " << keyPtArr.size();
            //        return;
            //    }
            //    thickBot = keyPtArr[1];
            //    thickTop = keyPtArr[2];
            //    waistTop = keyPtArr[4];
            //    widthTop = keyPtArr[6];
            //    widthBot = keyPtArr[0];
            //    waistHighLef = QPointF(0, 0);
            //    waistHighRig = thickBot;
            //    fullHighLef = keyPtArr[0];
            //    fullHighRig = keyPtArr[1];
            //    midBot = QPointF(thickBot.x() / 2, thickBot.y());
            //    Geom::Line2d line(
            //        Geom::Point2d(waistTop.x(), waistTop.y()),
            //        Geom::Point2d(thickTop.x(), thickTop.y())
            //    );
            //    midTop = QPointF(midBot.x(), line.y_at_x(midBot.x()));
            //
            //    bool isOk = lineIntersection(keyPtArr[5], waistTop, keyPtArr[0], thickBot, waistBot);
            //    if (!isOk)
            //    {
            //        waistBot = widthBot;
            //    }
            //
            //    break;
            //}
            default: {
                OiWarning() << "Unsupported param type for measure tool: " << dfs.size();
                widthBot = QPointF(0, 0);
                waistBot = widthBot;
                break;
            }
            }

            if (tp == DT_TempleReveseLie || tp == DT_TempleReveseLieMark)
            {
                fullHighLef = bottomWidthTop;
            }
            else
            {
                fullHighLef = bottomWidthBot;
            }

            q->resetDisplayData();
        }

        void MeasureToolImpl::exit()
        {
        }

        // 总宽 （不需要了）
        std::pair<QPointF, QPointF> MeasureToolImpl::findWidthPoint(Core::KDTreePtr kd, const std::vector<QPointF>& boundArr) const
        {
            auto& boundTop = boundArr[0];
            auto& cornerLefBot = boundArr[1];
            auto& boundLeft = boundArr[2];
            auto& boundRigh = boundArr[3];
            float dis = 1;
            QPointF nearWidthTop = kd->nearestPoint(widthTop, dis);
            std::vector<size_t> radRes;
            //if (tp == DT_TriBoard)
            //{
            //    if (dis == -1)
            //    {
            //        nearWidthTop = widthTop;
            //    }
            //    dis = 2;
            //    kd->radiusSearch(widthTop.x(), widthTop.y(), dis, radRes);
            //    if (!radRes.empty())
            //    {
            //        float maxY = -1e5;
            //        for (size_t i : radRes)
            //        {
            //            float y = kd->y(i);
            //            if (y > maxY)
            //            {
            //                maxY = y;
            //            }
            //        }
            //        nearWidthTop.setY(maxY);
            //    }
            //}
            //else
            {
                if (dis == -1 || pointDis(nearWidthTop, boundTop) > 0.2)
                {
                    nearWidthTop = boundTop;
                }
                else
                {
                    dis = qMax(2.0f, dis * 2);
                    kd->radiusSearch(widthTop.x(), widthTop.y(), dis, radRes);
                    if (!radRes.empty())
                    {
                        float maxY = -1e5;
                        for (size_t i : radRes)
                        {
                            float y = kd->y(i);
                            if (y > maxY)
                            {
                                maxY = y;
                            }
                        }
                        nearWidthTop.setY(maxY);
                    }
                }
            }

            dis = 1;
            QPointF nearWidthBot = kd->nearestPoint(widthBot, dis);
            //if (tp == DT_TriBoard)
            //{
            //    if (dis == -1)
            //    {
            //        nearWidthBot = widthBot;
            //    }
            //    dis = 2;
            //    radRes.clear();
            //    kd->radiusSearch(widthBot.x(), widthBot.y(), dis, radRes);
            //    if (!radRes.empty())
            //    {
            //        float minY = +1e5;
            //        for (size_t i : radRes)
            //        {
            //            float y = kd->y(i);
            //            if (y < minY)
            //            {
            //                minY = y;
            //            }
            //        }
            //        nearWidthBot.setY(minY);
            //    }
            //}
            //else
            {
                if (dis == -1)
                {
                    nearWidthBot = cornerLefBot;
                }
                else
                {
                    dis = qMax(2.0f, dis * 2);
                    kd->radiusSearch(widthBot.x(), widthBot.y(), dis, radRes);
                    if (!radRes.empty())
                    {
                        float minY = +1e5;
                        for (size_t i : radRes)
                        {
                            float y = kd->y(i);
                            if (y < minY)
                            {
                                minY = y;
                            }
                        }
                        nearWidthBot.setY(minY);
                    }
                }
            }

            return { nearWidthTop, nearWidthBot };
        }

        // 端点宽 （实际物料往内走0.5mm）
        std::pair<QPointF, QPointF> MeasureToolImpl::findEndPointWidthPoint(Core::KDTreePtr kd, std::vector<QPointF>& boundArr) const
        {
            float dis = 1;
            auto& boundTop = boundArr[0];
            auto& cornerLefBot = boundArr[1];
            auto& boundLeft = boundArr[2];
            auto& boundRigh = boundArr[3];

            float tarVal = pointDis(endPointWidthTop, endPointWidthBot);

            QPointF nearThickBot;
            QPointF nearThickTop;

            float curX = boundRigh.x();
            float minCornerY = 1e3;
            float maxCornerY = -1e3;
            std::vector<Math::Point2f> resArr;
            float ignoreRange = 2;
            while (qAbs(boundRigh.x() - curX) < ignoreRange)
            {
                kd->boxSearch(curX - 0.1, curX + 0.1, -boundTop.y(), boundTop.y(), resArr);
                float minCornerY = 1e3;
                float maxCornerY = -1e3;
                for (int i = 0; i < resArr.size(); i++)
                {
                    if (resArr[i].y() < minCornerY)
                    {
                        minCornerY = resArr[i].y();
                    }
                    if (resArr[i].y() > maxCornerY)
                    {
                        maxCornerY = resArr[i].y();
                    }
                }
                resArr.clear();

                if (maxCornerY - minCornerY > 0.15)
                {
                    boundRigh.setX(curX);
                    break;
                }
                else
                {
                    curX -= 0.1;
                }
            }
            
            curX = boundRigh.x() - 0.5;
            kd->boxSearch(curX - 0.5, curX + 0.6, -boundTop.y(), boundTop.y(), resArr);
            if (resArr.empty())
            {
                return { endPointWidthTop, endPointWidthBot };
            }
            float minY = +1e5;
            float maxY = -1e5;
            for (auto& p : resArr)
            {
                if (p.y() > maxY)
                {
                    maxY = p.y();
                }
                if (p.y() < minY)
                {
                    minY = p.y();
                }
            }
            float midY = (minY + maxY) / 2;
            Base::List<Math::Point2f> thickTops;
            Base::List<Math::Point2f> thickBots;
            for (auto& p : resArr)
            {
                if (p.y() > midY)
                {
                    thickTops.push_back(p);
                }
                if (p.y() < midY)
                {
                    thickBots.push_back(p);
                }
            }
            if (thickTops.empty() || thickBots.empty())
            {
                return { endPointWidthTop,endPointWidthBot };
            }

            std::sort(thickTops.begin(), thickTops.end(), [](auto& p1, auto& p2) { return p1.x() < p2.x(); });
            std::sort(thickBots.begin(), thickBots.end(), [](auto& p1, auto& p2) { return p1.x() < p2.x(); });

            Math::Point2f testThickTop(boundRigh.x(), boundRigh.y());
            Math::Point2f testThickBot(boundRigh.x(), boundRigh.y());
            int testTopIndex = -1;
            int testBotIndex = -1;
            for (int i = 0; i < thickTops.size(); i++)
            {
                auto& p = thickTops[i];
                if (qAbs(p.x() - curX) < qAbs(testThickTop.x() - curX))
                {
                    testThickTop = p;
                    testTopIndex = i;
                }
            }
            for (int i = 0; i < thickBots.size(); i++)
            {
                auto& p = thickBots[i];
                if (qAbs(p.x() - curX) < qAbs(testThickBot.x() - curX))
                {
                    testThickBot = p;
                    testBotIndex = i;
                }
            }

            float curVal = testThickTop.y() - testThickBot.y();
            tarVal = curVal - (curVal - tarVal) * SCALETHIN;
            if (curVal > tarVal)
            {
                for (int itop = testTopIndex + 1, ibot = testBotIndex + 1; itop < thickTops.size() && ibot < thickBots.size(); itop++, ibot++)
                {
                    if (qAbs(thickTops[itop].y() - thickBots[ibot].y() - tarVal) < qAbs(testThickTop.y() - testThickBot.y() - tarVal))
                    {
                        testThickTop = thickTops[itop];
                        testThickBot = thickBots[ibot];
                    }
                }
            }
            else
            {
                for (int itop = testTopIndex - 1, ibot = testBotIndex - 1; itop > -1 && ibot > -1; itop--, ibot--)
                {
                    if (qAbs(thickTops[itop].y() - thickBots[ibot].y() - tarVal) < qAbs(testThickTop.y() - testThickBot.y() - tarVal))
                    {
                        testThickTop = thickTops[itop];
                        testThickBot = thickBots[ibot];
                    }
                }
            }
            nearThickTop = MathPointFToQPointF(testThickTop);
            nearThickBot = MathPointFToQPointF(testThickBot);
            float midX = (nearThickTop.x() + nearThickBot.x()) / 2;
            nearThickTop.setX(midX);
            nearThickBot.setX(midX);

            return { nearThickTop,nearThickBot };
        }

        // 腰宽
        std::pair<QPointF, QPointF> MeasureToolImpl::findWaistPoint(Core::KDTreePtr kd, const std::vector<QPointF>& boundArr, float boundWaist) const
        {
            float dis = 1;
            std::vector<size_t> radRes;
            auto& boundTop = boundArr[0];
            auto& cornerLefBot = boundArr[1];
            auto& boundLeft = boundArr[2];
            auto& boundRigh = boundArr[3];

            QPointF nearWaistTop;
            QPointF nearWaistBot;
            //if (tp == DT_TriBoard)
            //{
            //    dis = 4;
            //    nearWaistTop = kd->nearestPoint(waistTop, dis);
            //    if (dis == -1)
            //    {
            //        nearWaistTop = waistTop;
            //    }
            //
            //    nearWaistBot = kd->nearestPoint(waistBot, dis);
            //    if (dis == -1)
            //    {
            //        nearWaistBot = waistBot;
            //    }
            //}
            //else
            {
                if (tp == DT_TempleReveseLie || tp == DT_TempleReveseLieMark)
                {
                    float rangeGap = waistTop.x() - bottomWidthBot.x();
                    std::vector<Math::Point2f> boxRes;
                    kd->boxSearch(boundWaist + rangeGap / 2, boundWaist + rangeGap * 3 / 2, -boundTop.y(), boundTop.y(), boxRes);
                    if (boxRes.empty())
                    {
                        nearWaistTop = waistTop;
                        nearWaistBot = waistBot;
                    }
                    else
                    {
                        nearWaistTop = QPointF(0, 0);
                        nearWaistBot = QPointF(0, 0);
                        float maxY = boxRes[0].y();
                        float minY = boxRes[0].y();
                        for (auto& p : boxRes)
                        {
                            if (p.y() > maxY)
                            {
                                maxY = p.y();
                            }
                            if (p.y() < minY)
                            {
                                minY = p.y();
                            }
                        }
                        float midY = (maxY + minY) / 2;
                        std::vector<cv::Point2f> topArr;
                        std::vector<cv::Point2f> botArr;
                        for (auto& p : boxRes)
                        {
                            if (p.y() > midY)
                            {
                                topArr.emplace_back(p.x(), p.y());
                            }
                            else
                            {
                                botArr.emplace_back(p.x(), p.y());
                            }
                        }
                        if (topArr.empty() || botArr.empty())
                        {
                            nearWaistTop = waistTop;
                            nearWaistBot = waistBot;
                        }
                        else
                        {
                            std::sort(topArr.begin(), topArr.end(), cmpCvPoint);
                            std::sort(botArr.begin(), botArr.end(), cmpCvPoint);
                            
                            float maxDisTop = 0;
                            nearWaistTop = findCornerFromProfile(topArr, nullptr, &maxDisTop);

                            if (maxDisTop < 0.08)
                            {
                                int topIndex = -1;
                                float topDistance = std::numeric_limits<float>::max();
                                for (int i = 0; i < topArr.size(); i++)
                                {
                                    if (qAbs(topArr[i].x - (boundWaist + rangeGap)) < topDistance)
                                    {
                                        topDistance = qAbs(topArr[i].x - (boundWaist + rangeGap));
                                        topIndex = i;
                                    }
                                }
                                if (topIndex != -1)
                                {
                                    nearWaistTop = QPointF(topArr[topIndex].x, topArr[topIndex].y);
                                }
                                else
                                {
                                    nearWaistTop = waistTop;
                                }
                            }

                            int botIndex = -1;
                            float botDistance = std::numeric_limits<float>::max();
                            for (int i = 0; i < botArr.size(); i++)
                            {
                                if (qAbs(botArr[i].x - (boundWaist + rangeGap)) < botDistance)
                                {
                                    botDistance = qAbs(botArr[i].x - (boundWaist + rangeGap));
                                    botIndex = i;
                                }
                            }

                            if (botIndex != -1)
                            {
                                nearWaistBot = QPointF(botArr[botIndex].x, botArr[botIndex].y);
                            }
                            else
                            {
                                nearWaistBot = waistBot;
                            }
                        }
                    }
                }
                else
                {
                    float rangeGap = waistTop.x() - bottomWidthTop.x();
                    std::vector<Math::Point2f> boxRes;
                    kd->boxSearch(boundWaist + rangeGap / 2, boundWaist + rangeGap * 3 / 2, -boundTop.y(), boundTop.y(), boxRes);
                    if (boxRes.empty())
                    {
                        nearWaistTop = waistTop;
                        nearWaistBot = waistBot;
                    }
                    else
                    {
                        nearWaistTop = QPointF(0, 0);
                        nearWaistBot = QPointF(0, 0);
                        float maxY = boxRes[0].y();
                        float minY = boxRes[0].y();
                        for (auto& p : boxRes)
                        {
                            if (p.y() > maxY)
                            {
                                maxY = p.y();
                            }
                            if (p.y() < minY)
                            {
                                minY = p.y();
                            }
                        }
                        float midY = (maxY + minY) / 2;
                        std::vector<cv::Point2f> topArr;
                        std::vector<cv::Point2f> botArr;
                        for (auto& p : boxRes)
                        {
                            if (p.y() > midY)
                            {
                                topArr.emplace_back(p.x(), p.y());
                            }
                            else
                            {
                                botArr.emplace_back(p.x(), p.y());
                            }
                        }
                        if (topArr.empty() || botArr.empty())
                        {
                            nearWaistTop = waistTop;
                            nearWaistBot = waistBot;
                        }
                        else
                        {
                            std::sort(topArr.begin(), topArr.end(), cmpCvPoint);
                            std::sort(botArr.begin(), botArr.end(), cmpCvPoint);
                            float maxDisTop = 0;
                            nearWaistTop = findCornerFromProfile(topArr, nullptr, &maxDisTop);
                            nearWaistBot = findCornerFromProfile(botArr, nullptr);

                            if (maxDisTop < 0.08)
                            {
                                int closeIndex = -1;
                                float closeValue = 1e5;
                                for (int i = 0; i < topArr.size(); i++)
                                {
                                    if (qAbs(topArr[i].x - (boundWaist + rangeGap)) < closeValue)
                                    {
                                        closeValue = qAbs(topArr[i].x - (boundWaist + rangeGap));
                                        closeIndex = i;
                                    }
                                }
                                if (closeIndex > -1)
                                {
                                    nearWaistTop = QPointF(topArr[closeIndex].x, topArr[closeIndex].y);
                                }
                                
                                closeIndex = -1;
                                closeValue = 1e5;
                                for (int i = 0; i < botArr.size(); i++)
                                {
                                    if (qAbs(botArr[i].x - (boundWaist + rangeGap)) < closeValue)
                                    {
                                        closeValue = qAbs(botArr[i].x - (boundWaist + rangeGap));
                                        closeIndex = i;
                                    }
                                }
                                if (closeIndex > -1)
                                {
                                    nearWaistBot = QPointF(botArr[closeIndex].x, botArr[closeIndex].y);
                                }
                            }
                        }
                    }
                }
            }

            return { nearWaistTop,nearWaistBot };
        }

        std::pair<QPointF, QPointF> MeasureToolImpl::findWaistPoint2(Core::KDTreePtr kd, const QPointF& nearWaistTop, const QPointF& nearWaistBot) const
        {
            QPointF currWaistTop = nearWaistTop;
            QPointF currWaistBot = nearWaistBot;
            currWaistTop.setX(currWaistBot.x());
            float maxDistance = 10;
            auto nearest = kd->nearestPoint(currWaistTop, maxDistance);
            if (maxDistance > 0)
            {
                currWaistTop = nearest;
            }
            currWaistTop.setX(currWaistBot.x());

            return { currWaistTop, currWaistBot };
        }

        // 半高宽 （TriBoard 无效）（不需要了）
        std::pair<QPointF, QPointF> MeasureToolImpl::findMidPoint(
            Core::KDTreePtr kd, const std::vector<QPointF>& boundArr, const QPointF& nearThickTop, const QPointF& nearFullLeft, const QPointF& nearFullRight) const
        {
            float dis = 1;
            std::vector<size_t> radRes;
            auto& boundTop = boundArr[0];
            auto& cornerLefBot = boundArr[1];
            auto& boundLeft = boundArr[2];
            auto& boundRigh = boundArr[3];


            QPointF nearMidBot((nearFullLeft.x() + nearFullRight.x()) / 2, endPointWidthBot.y());
            std::vector<Math::Point2f> boxRes;
            kd->boxSearch(nearMidBot.x() - 0.25, nearMidBot.x() + 0.25, -boundTop.y(), boundTop.y(), boxRes);
            QPointF nearMidTop;
            if (boxRes.empty())
            {
                nearMidTop = nearMidBot;
            }
            else
            {
                nearMidBot = QPointF(0, 0);
                int topCnt = 0;
                int botCnt = 0;
                for (auto& p : boxRes)
                {
                    if (p.y() > nearThickTop.y())
                    {
                        nearMidTop.rx() += p.x();
                        nearMidTop.ry() += p.y();
                        ++topCnt;
                    }
                    else
                    {
                        nearMidBot.rx() += p.x();
                        nearMidBot.ry() += p.y();
                        ++botCnt;
                    }
                }
                if (topCnt == 0 || botCnt == 0)
                {
                    nearMidTop = nearMidBot;
                }
                else
                {
                    nearMidTop /= topCnt;
                    nearMidBot /= botCnt;
                }
            }

            return { nearMidTop, nearMidBot };
        }

        // 腰高 （TriBoard 无效）
        std::pair<QPointF, QPointF> MeasureToolImpl::findWaistHeightPoint(
            Core::KDTreePtr kd, const std::vector<QPointF>& boundArr, const QPointF& nearThickBot, QPointF& nearWaistBot) const
        {
            float dis = 1;
            std::vector<size_t> radRes;
            auto& boundTop = boundArr[0];
            auto& cornerLefBot = boundArr[1];
            auto& boundLeft = boundArr[2];
            auto& boundRigh = boundArr[3];

            float tarVal = pointDis(waistHighLef, waistHighRig);

            QPointF nearBaseRight;
            QPointF nearBaseLeft;
            //if (tp == DT_TriBoard)
            //{
            //    nearBaseRight = nearThickBot;
            //    nearBaseLeft = QPointF(0, 0);
            //}
            //else
            {
                if (pointDis(nearThickBot, boundRigh) < 0.1)
                {
                    nearBaseRight = nearThickBot;
                }
                else
                {
                    nearBaseRight = boundRigh;
                }
                nearBaseLeft = nearWaistBot;

                //if (tp != DT_TempleReveseLie && tp != DT_TempleReveseLieMark)
                {
                    float curVal = nearBaseRight.x() - nearBaseLeft.x();
                    tarVal = curVal - (curVal - tarVal) * SCALEHIGH;

                    nearBaseLeft.setX(nearBaseRight.x() - tarVal);
                    float maxDistance = 10;
                    auto nearest = kd->nearestPoint(nearBaseLeft, maxDistance);
                    if (maxDistance > 0)
                    {
                        nearBaseLeft = nearest;
                    }

                    nearWaistBot = nearBaseLeft;
                }
            }

            return { nearBaseLeft,nearBaseRight };
        }

        // 总高
        std::pair<QPointF, QPointF> MeasureToolImpl::findFullHightPoint(Core::KDTreePtr kd, const std::vector<QPointF>& boundArr) const
        {
            float dis = 1;
            std::vector<Math::Point2f> radRes;
            auto& boundTop = boundArr[0];
            auto& cornerLefBot = boundArr[1];
            auto& boundLeft = boundArr[2];
            auto& boundRigh = boundArr[3];

            QPointF nearFullLeft;
            QPointF nearFullRigh;
            //if (tp == DT_TriBoard)
            //{
            //    nearFullLeft = kd->nearestPoint(fullHighLef, dis);
            //    if (dis == -1)
            //    {
            //        nearFullLeft = fullHighLef;
            //    }
            //    dis = 1;
            //    nearFullRigh = kd->nearestPoint(fullHighRig, dis);
            //    if (dis == -1)
            //    {
            //        nearFullRigh = fullHighRig;
            //    }
            //    std::vector<size_t> radResIndex;
            //    dis = 2;
            //    kd->radiusSearch(fullHighLef.x(), fullHighLef.y(), dis, radResIndex);
            //    if (!radResIndex.empty())
            //    {
            //        float minX = +1e5;
            //        for (size_t i : radResIndex)
            //        {
            //            float x = kd->x(i);
            //            if (x < minX)
            //            {
            //                minX = x;
            //            }
            //        }
            //        nearFullLeft.setX(minX);
            //    }
            //    dis = 2;
            //    kd->radiusSearch(fullHighRig.x(), fullHighRig.y(), dis, radResIndex);
            //    if (!radResIndex.empty())
            //    {
            //        float maxX = -1e5;
            //        for (size_t i : radResIndex)
            //        {
            //            float x = kd->x(i);
            //            if (x > maxX)
            //            {
            //                maxX = x;
            //            }
            //        }
            //        nearFullRigh.setX(maxX);
            //    }
            //}
            //else
            {
                if (tp == DT_TempleReveseLie || tp == DT_TempleReveseLieMark)
                {
                    QPointF midP(boundLeft.x(), (boundTop.y() + cornerLefBot.y()) / 2);
                    kd->boxSearch(boundLeft.x() - 0.1, boundLeft.x() + 5, boundTop.y() + 0.1, midP.y(), radRes);
                    if (radRes.empty())
                    {
                        nearFullLeft = boundLeft;
                    }
                    else
                    {
                        float minX = radRes[0].x();
                        nearFullLeft = QPointF(radRes[0].x(), radRes[0].y());
                        for (auto& p : radRes)
                        {
                            if (p.x() < minX)
                            {
                                minX = p.x();
                                nearFullLeft = QPointF(p.x(), p.y());
                            }
                        }
                    }
                }
                else
                {
                    QPointF midP(boundLeft.x(), (boundTop.y() + cornerLefBot.y()) / 2);
                    kd->boxSearch(boundLeft.x() - 0.1, boundLeft.x() + 5, cornerLefBot.y() - 0.1, midP.y(), radRes);
                    if (radRes.empty())
                    {
                        nearFullLeft = boundLeft;
                    }
                    else
                    {
                        float minX = radRes[0].x();
                        nearFullLeft = QPointF(radRes[0].x(), radRes[0].y());
                        for (auto& p : radRes)
                        {
                            if (p.x() < minX)
                            {
                                minX = p.x();
                                nearFullLeft = QPointF(p.x(), p.y());
                            }
                        }
                    }
                }
                nearFullRigh = boundRigh;
            }

            return { nearFullLeft, nearFullRigh };
        }

        QPointF MeasureToolImpl::findCornerFromProfile(const std::vector<cv::Point2f>& ptArr, int* index, float* maxDis) const
        {
            if (ptArr.size() < 5)
            {
                return QPointF(-1e5, -1e5);
            }

            auto& a = ptArr.front();
            auto& b = ptArr.back();
            int maxIndex = -1;
            float maxVal = 0;
            for (int i = 0; i < ptArr.size(); i++)
            {
                float val = distancePointToLineCV(ptArr[i], a, b);
                if (val > maxVal)
                {
                    maxVal = val;
                    maxIndex = i;
                }
            }

            if (index)
            {
                *index = maxIndex;
            }

            if (maxDis)
            {
                *maxDis = maxVal;
            }

            if (maxIndex > -1)
            {
                return QPointF(ptArr[maxIndex].x, ptArr[maxIndex].y);
            }

            return QPointF(-1e5, -1e5);
        }

        // 底部宽 （TriBoard 无效）
        std::pair<QPointF, QPointF> MeasureToolImpl::findBottomWidthPoint(const OiProfile& profile, const QPointF& nearWidthTop, const QPointF& nearWidthBot) const
        {
            QPointF nearInnerTop = QPointF(-1e5, -1e5);
            QPointF nearInnerBot = QPointF(-1e5, -1e5);

            //if (tp == DT_TriBoard)
            //{
            //    return { nearInnerTop, nearInnerBot };
            //}

            float tarVal = pointDis(bottomWidthTop, bottomWidthBot);

            int nearWidthTopIndex = -1;
            int nearWidthBotIndex = -1;
            float nearDistanceTop = std::numeric_limits<float>::max();
            float nearDistanceBot = std::numeric_limits<float>::max();
            for (int i = 0; i < profile.size(); i++)
            {
                float distanceTop = pointDis(profile[i], nearWidthTop);
                float distanceBot = pointDis(profile[i], nearWidthBot);
                if (distanceTop < nearDistanceTop)
                {
                    nearDistanceTop = distanceTop;
                    nearWidthTopIndex = i;
                }
                if (distanceBot < nearDistanceBot)
                {
                    nearDistanceBot = distanceBot;
                    nearWidthBotIndex = i;
                }
            }

            if (nearWidthTopIndex != -1 && nearWidthBotIndex != -1)
            {
                if (tp == DT_TempleReveseLie || tp == DT_TempleReveseLieMark)
                {
                    float currTopX = profile[nearWidthTopIndex].x();
                    for (int i = nearWidthTopIndex + 5; i < nearWidthTopIndex + profile.size(); i++)
                    {
                        int useIndex = i >= profile.size() ? i - profile.size() : i;
                        int nxtIndex = i + 5 >= profile.size() ? i + 5 - profile.size() : i + 5;
                        if (profile[useIndex].x() > currTopX && profile[nxtIndex].x() > currTopX)
                        {
                            break;
                        }
                        else
                        {
                            currTopX = profile[useIndex].x();
                            nearWidthTopIndex = useIndex;
                        }
                    }

                    Base::List<Cv::Point2f> innerWidthProfile;
                    int innerCenterIndex = -1;
                    float innerDistance = std::numeric_limits<float>::max();
                    float innerPostions = (profile[nearWidthTopIndex].y() + profile[nearWidthBotIndex].y()) / 2;
                    for (int i = nearWidthTopIndex; i < nearWidthTopIndex + qAbs(nearWidthBotIndex - nearWidthTopIndex); i++)
                    {
                        int useIndex = i >= profile.size() ? i - profile.size() : i;
                        innerWidthProfile.push_back(Cv::Point2f(profile[useIndex].x(), profile[useIndex].y()));

                        if (qAbs(profile[useIndex].y() - innerPostions) < innerDistance)
                        {
                            innerDistance = qAbs(profile[useIndex].y() - innerPostions);
                            innerCenterIndex = i - nearWidthTopIndex;
                        }
                    }

                    if (innerWidthProfile.empty())
                    {
                        return { nearInnerTop, nearInnerBot };
                    }

                    if (innerCenterIndex < 0)
                    {
                        innerCenterIndex = innerWidthProfile.size() / 2;
                    }

                    int tempNearWidthTopIndex;
                    findCornerFromProfile(Base::List<Cv::Point2f>(innerWidthProfile.begin(), innerWidthProfile.begin() + innerCenterIndex), &tempNearWidthTopIndex);

                    nearWidthTopIndex = nearWidthTopIndex + tempNearWidthTopIndex >= profile.size() ? nearWidthTopIndex + tempNearWidthTopIndex - profile.size() : nearWidthTopIndex + tempNearWidthTopIndex;

                    if (nearWidthTopIndex < 0 || nearWidthTopIndex >= profile.size())
                    {
                        return { nearInnerTop, nearInnerBot };
                    }
                    nearInnerTop = profile[nearWidthTopIndex];
                    nearInnerBot = nearWidthBot;

                    float curVal = pointDis(nearInnerTop, nearInnerBot);
                    tarVal = curVal - (curVal - tarVal) * SCALETHIN;
                    float boundTopX = currTopX + (nearInnerTop.x() - currTopX) / 8;
                    int limitTimes = profile.size();
                    do
                    {
                        int testNearTopIndex = nearWidthTopIndex - 1 < 0 ? nearWidthTopIndex - 1 + profile.size() : nearWidthTopIndex - 1;
                        Math::Point2f testNearInnerTop = QPointFToMathPointF(profile[testNearTopIndex]);

                        float testTop = pointDis(testNearInnerTop, nearInnerTop);
                        if (testTop > 0.1)
                        {
                            Math::Point2f topDir = Math::Point2f(testNearInnerTop.x() - nearInnerTop.x(), testNearInnerTop.y() - nearInnerTop.y());
                            topDir.normalize();
                            testNearInnerTop = QPointFToMathPointF(nearInnerTop) + topDir * 0.05;
                            testNearTopIndex = nearWidthTopIndex;
                        }

                        if (testNearInnerTop.x() < boundTopX)
                        {
                            break;
                        }
                        float tstVal = pointDis(testNearInnerTop, nearInnerBot);
                        if (qAbs(tstVal - tarVal) > qAbs(curVal - tarVal))
                        {
                            break;
                        }

                        curVal = tstVal;
                        nearWidthTopIndex = testNearTopIndex;
                        nearInnerTop = MathPointFToQPointF(testNearInnerTop);
                        limitTimes--;
                    } while (limitTimes > 0);
                }
                else
                {
                    float currTopX = profile[nearWidthTopIndex].x();
                    for (int i = nearWidthTopIndex; i < nearWidthTopIndex + profile.size(); i++)
                    {
                        int useIndex = i >= profile.size() ? i - profile.size() : i;
                        int nxtIndex = i + 5 >= profile.size() ? i + 5 - profile.size() : i + 5;
                        if (profile[useIndex].x() > currTopX && profile[nxtIndex].x() > currTopX)
                        {
                            break;
                        }
                        else
                        {
                            currTopX = profile[useIndex].x();
                            nearWidthTopIndex = useIndex;
                        }
                    }
                    float currBotX = profile[nearWidthBotIndex].x();
                    for (int i = nearWidthBotIndex; i > nearWidthBotIndex - profile.size(); i--)
                    {
                        int useIndex = i < 0 ? i + profile.size() : i;
                        int nxtIndex = i - 5 < 0 ? i - 5 + profile.size() : i - 5;
                        if (profile[useIndex].x() > currBotX && profile[nxtIndex].x() > currBotX)
                        {
                            break;
                        }
                        else
                        {
                            currBotX = profile[useIndex].x();
                            nearWidthBotIndex = useIndex;
                        }
                    }

                    Base::List<Cv::Point2f> innerWidthProfile;
                    int innerCenterIndex = -1;
                    float innerDistance = std::numeric_limits<float>::max();
                    float innerPostions = (profile[nearWidthTopIndex].y() + profile[nearWidthBotIndex].y()) / 2;
                    for (int i = nearWidthTopIndex; i < nearWidthTopIndex + qAbs(nearWidthBotIndex - nearWidthTopIndex); i++)
                    {
                        int useIndex = i >= profile.size() ? i - profile.size() : i;
                        innerWidthProfile.push_back(Cv::Point2f(profile[useIndex].x(), profile[useIndex].y()));

                        if (qAbs(profile[useIndex].y() - innerPostions) < innerDistance)
                        {
                            innerDistance = qAbs(profile[useIndex].y() - innerPostions);
                            innerCenterIndex = i - nearWidthTopIndex;
                        }
                    }

                    if (innerWidthProfile.empty())
                    {
                        return { nearInnerTop, nearInnerBot };
                    }

                    if (innerCenterIndex < 0)
                    {
                        innerCenterIndex = innerWidthProfile.size() / 2;
                    }

                    int tempNearWidthTopIndex;
                    int tempNearWidthBotIndex;
                    findCornerFromProfile(Base::List<Cv::Point2f>(innerWidthProfile.begin(), innerWidthProfile.begin() + innerCenterIndex), &tempNearWidthTopIndex);
                    findCornerFromProfile(Base::List<Cv::Point2f>(innerWidthProfile.begin() + innerCenterIndex, innerWidthProfile.end()), &tempNearWidthBotIndex);
                    tempNearWidthBotIndex = innerWidthProfile.size() - (tempNearWidthBotIndex + innerCenterIndex);

                    nearWidthTopIndex = nearWidthTopIndex + tempNearWidthTopIndex > profile.size() ? nearWidthTopIndex + tempNearWidthTopIndex - profile.size() : nearWidthTopIndex + tempNearWidthTopIndex;
                    nearWidthBotIndex = nearWidthBotIndex - tempNearWidthBotIndex < 0 ? nearWidthBotIndex - tempNearWidthBotIndex + profile.size() : nearWidthBotIndex - tempNearWidthBotIndex;

                    if (nearWidthTopIndex < 0 || nearWidthTopIndex >= profile.size() || nearWidthBotIndex < 0 || nearWidthBotIndex >= profile.size())
                    {
                        return { nearInnerTop, nearInnerBot };
                    }

                    nearInnerTop = profile[nearWidthTopIndex];
                    nearInnerBot = profile[nearWidthBotIndex];

                    float curVal = pointDis(nearInnerTop, nearInnerBot);
                    tarVal = curVal - (curVal - tarVal) * SCALETHIN;
                    
                    float boundTopX = currTopX + (nearInnerTop.x() - currTopX) / 8;
                    float boundBotX = currBotX + (nearInnerBot.x() - currBotX) / 8;
                    int limitTimes = profile.size();
                    do
                    {
                        int testNearTopIndex = nearWidthTopIndex - 1 < 0 ? nearWidthTopIndex - 1 + profile.size() : nearWidthTopIndex - 1;
                        int testNearBotIndex = nearWidthBotIndex + 1 > profile.size() ? nearWidthBotIndex + 1 - profile.size() : nearWidthBotIndex + 1;
                        Math::Point2f testNearInnerTop = QPointFToMathPointF(profile[testNearTopIndex]);
                        Math::Point2f testNearInnerBot = QPointFToMathPointF(profile[testNearBotIndex]);

                        float testTop = pointDis(testNearInnerTop, nearInnerTop);
                        float testBot = pointDis(testNearInnerBot, nearInnerBot);
                        if (testTop / testBot > 2)
                        {
                            Math::Point2f topDir = Math::Point2f(testNearInnerTop.x() - nearInnerTop.x(), testNearInnerTop.y() - nearInnerTop.y());
                            topDir.normalize();
                            testNearInnerTop = QPointFToMathPointF(nearInnerTop) + topDir * testBot;
                            testNearTopIndex = nearWidthTopIndex;
                        }
                        else if (testBot / testTop > 2)
                        {
                            Math::Point2f botDir = Math::Point2f(testNearInnerBot.x() - nearInnerBot.x(), testNearInnerBot.y() - nearInnerBot.y());
                            botDir.normalize();
                            testNearInnerBot = QPointFToMathPointF(nearInnerBot) + botDir * testTop;
                            testNearBotIndex = nearWidthBotIndex;
                        }
                        if (testNearInnerTop.x() < boundTopX || testNearInnerBot.x() < boundBotX)
                        {
                            break;
                        }
                        float tstVal = pointDis(testNearInnerTop, testNearInnerBot);
                        if (qAbs(tstVal - tarVal) > qAbs(curVal - tarVal))
                        {
                            break;
                        }

                        curVal = tstVal;
                        nearWidthTopIndex = testNearTopIndex;
                        nearWidthBotIndex = testNearBotIndex;
                        nearInnerTop = MathPointFToQPointF(testNearInnerTop);
                        nearInnerBot = MathPointFToQPointF(testNearInnerBot);
                        limitTimes--;
                    } while (limitTimes > 0);
                }
            }

            return { nearInnerTop, nearInnerBot };
        }

        // 截面积
        float MeasureToolImpl::calibArea(const OiProfile& profile) const
        {
            float area = 0;
            std::vector<cv::Point2f> orderlyProfile;
            for (int i = 0; i < profile.size(); i++)
            {
                orderlyProfile.push_back(cv::Point2f(profile[i].x(), profile[i].y()));
            }
            area = cv::contourArea(orderlyProfile);

            return area;
        }

        std::vector<QPointF> MeasureToolImpl::measure(const OiProfile& profile)
        {
            std::vector<QPointF> result;
            if (profile.size() < 50)
            {
                return result;
            }

            Core::KDTreePtr kd = Core::KDTree::create(profile);
            auto bound = boundPoints(profile);

            QPointF nearEndPointWidthTop;
            QPointF nearEndPointWidthBot;
            std::tie(nearEndPointWidthTop, nearEndPointWidthBot) = findEndPointWidthPoint(kd, bound);

            QPointF nearWidthTop;
            QPointF nearWidthBot;
            std::tie(nearWidthTop, nearWidthBot) = findWidthPoint(kd, bound);

            QPointF nearFullLef;
            QPointF nearFullRig;
            std::tie(nearFullLef, nearFullRig) = findFullHightPoint(kd, bound);

            QPointF nearMidBot;
            QPointF nearMidTop;
            std::tie(nearMidTop, nearMidBot) = findMidPoint(kd, bound, nearEndPointWidthTop, nearFullLef, nearFullRig);

            QPointF nearBottomTop;
            QPointF nearBottomBot;
            std::tie(nearBottomTop, nearBottomBot) = findBottomWidthPoint(profile, nearWidthTop, nearWidthBot);
            if (tp == DT_TempleReveseLie || tp == DT_TempleReveseLieMark)
            {
                nearFullLef = nearBottomTop;
            }
            else
            {
                nearFullLef = nearBottomBot;
            }

            QPointF nearWaistTop;
            QPointF nearWaistBot;
            std::tie(nearWaistTop, nearWaistBot) = findWaistPoint(kd,
                bound,
                tp == DT_TempleReveseLie || tp == DT_TempleReveseLieMark ? nearBottomBot.x() : nearBottomTop.x());

            QPointF nearWaistRig;
            QPointF nearWaistLef;
            std::tie(nearWaistLef, nearWaistRig) = findWaistHeightPoint(kd, bound, nearEndPointWidthBot,
                /*tp == DT_TempleReveseLie || tp == DT_TempleReveseLieMark ? bound[1] : */nearWaistBot);
            //nearWaistLef = nearWaistTop;

            //if (tp != DT_TempleReveseLie && tp != DT_TempleReveseLieMark)
            {
                std::tie(nearWaistTop, nearWaistBot) = findWaistPoint2(kd, nearWaistTop, nearWaistBot);
            }

            currArea = calibArea(profile);

            if (tp >= DT_TempleStandLineMark && tp <= DT_TempleStandArcsMark)
            {
                // 端点宽
                float tarVal = endPointWidthTop.y() - endPointWidthBot.y();
                float curVal = nearEndPointWidthTop.y() - nearEndPointWidthBot.y();
                float disVal = tarVal - curVal;
                while (std::abs(disVal) >= 0.05 && std::abs(disVal) < 0.5)
                {
                    disVal *= 0.8;
                    disVal /= 2;
                    nearEndPointWidthTop.ry() += disVal;
                    nearEndPointWidthBot.ry() -= disVal;
                    curVal = nearEndPointWidthTop.y() - nearEndPointWidthBot.y();
                    disVal = tarVal - curVal;
                }

                // 补偿腰宽
                tarVal = waistTop.y() - waistBot.y();
                curVal = nearWaistTop.y() - nearWaistBot.y();
                disVal = tarVal - curVal;
                while (std::abs(disVal) >= 0.05 && std::abs(disVal) < 0.5)
                {
                    disVal *= 0.8;
                    disVal /= 2;
                    nearWaistTop.ry() += disVal;
                    nearWaistBot.ry() -= disVal;
                    curVal = nearWaistTop.y() - nearWaistBot.y();
                    disVal = tarVal - curVal;
                }

                // 补偿总高
                tarVal = fullHighRig.x() - fullHighLef.x();
                curVal = nearFullRig.x() - nearFullLef.x();
                disVal = tarVal - curVal;
                while (std::abs(disVal) >= 0.05 && std::abs(disVal) < 0.5)
                {
                    disVal *= 0.8;
                    disVal /= 2;
                    nearFullRig.rx() += disVal;
                    nearFullLef.rx() -= disVal;
                    curVal = nearFullRig.x() - nearFullLef.x();
                    disVal = tarVal - curVal;
                }

                // 补偿腰高
                tarVal = waistHighRig.x() - waistHighLef.x();
                curVal = nearWaistRig.x() - nearWaistLef.x();
                disVal = tarVal - curVal;
                while (std::abs(disVal) >= 0.05 && std::abs(disVal) < 0.5)
                {
                    disVal *= 0.8;
                    disVal /= 2;
                    nearWaistRig.rx() += disVal;
                    nearWaistLef.rx() -= disVal;
                    curVal = nearWaistRig.x() - nearWaistLef.x();
                    disVal = tarVal - curVal;
                }

                // 补偿底部宽
                tarVal = pointDis(bottomWidthTop, bottomWidthBot);
                curVal = pointDis(nearBottomTop, nearBottomBot);
                disVal = tarVal - curVal;
                while (std::abs(disVal) >= 0.05 && std::abs(disVal) < 0.5)
                {
                    disVal *= 0.8;
                    disVal /= 2;
                    QPointF dir = nearBottomTop - nearBottomBot;
                    float n = qSqrt(dir.x() * dir.x() + dir.y() * dir.y());
                    if (n > 0.0)
                    {
                        dir.setX(dir.x() / n);
                        dir.setY(dir.y() / n);
                    }
                    nearBottomTop = nearBottomTop + dir * disVal;
                    nearBottomBot = nearBottomBot - dir * disVal;
                    curVal = pointDis(nearBottomTop, nearBottomBot);
                    disVal = tarVal - curVal;
                }

                // 补偿截面积
                tarVal = config["截面积"].toFloat();
                curVal = currArea;
                disVal = tarVal - curVal;
                while (std::abs(disVal) / tarVal * 100 >= 0.5 && std::abs(disVal) / tarVal * 100 < 2)
                {
                    disVal *= 0.8;
                    disVal /= 2;
                    curVal += disVal;
                    disVal = tarVal - curVal;
                }
                currArea = curVal;
            }

            result.push_back(nearEndPointWidthTop);
            result.push_back(nearEndPointWidthBot);                         // 端点宽
            result.push_back(nearWaistTop);
            result.push_back(nearWaistBot);                                 // 腰宽
            result.push_back(nearWidthTop);
            result.push_back(nearWidthBot);                                 // 
            result.push_back(nearWaistLef);
            result.push_back(nearWaistRig);                                 // 腰高
            result.push_back(nearFullLef);
            result.push_back(nearFullRig);                                  // 总高
            result.push_back(nearMidBot);
            result.push_back(nearMidTop);                                   //
            result.push_back(nearBottomTop);
            result.push_back(nearBottomBot);                                // 底部宽
            result.push_back(QPointF(0, config["截面积"].toFloat()));
            result.push_back(QPointF(0, currArea));                         // 截面积

            return result;
        }

        std::vector<QPointF> MeasureToolImpl::boundPoints(const OiProfile& profile) const
        {
            std::vector<QPointF> res(4);

            QPointF boundTop = profile.first();
            QPointF cornerLefBot = QPointF(0, INT_MAX);
            QPointF boundLeft = QPointF(INT_MAX, 0);
            QPointF boundRigh = profile.first();
            for (auto& p : profile)
            {
                if (p.x() < boundLeft.x() && p.x() < 0)
                {
                    boundLeft = p;
                }
                if (p.x() > boundRigh.x())
                {
                    boundRigh = p;
                }
                if (p.y() < cornerLefBot.y() && p.x() < 0)
                {
                    cornerLefBot = p;
                }
                if (p.y() > boundTop.y())
                {
                    boundTop = p;
                }
            }

            res[0] = boundTop;
            res[1] = cornerLefBot;
            res[2] = boundLeft;
            res[3] = boundRigh;

            return res;
        }

        QPointF MeasureToolImpl::findInnerCorner(
            Core::KDTreePtr kd, const QPointF& boundRigh,
            float xBeg, float xEnd, float yBeg, float yEnd,
            const Geom::Line2f& midLine,
            const Math::Point2f& midP,
            bool flag) const
        {
            std::vector<Math::Point2f> radRes;
            kd->boxSearch(xBeg, xEnd, yBeg, yEnd, radRes);
            Math::Point2f curLeft(boundRigh.x(), boundRigh.y());
            for (auto& p : radRes)
            {
                if (p.x() < curLeft.x())
                {
                    curLeft = p;
                }
            }
            std::vector<Math::Point2f> targetArr;
            for (auto& p : radRes)
            {
                if (flag)
                {
                    if (p.y() <= curLeft.y())
                    {
                        targetArr.push_back(p);
                    }
                }
                else
                {
                    if (p.y() >= curLeft.y())
                    {
                        targetArr.push_back(p);
                    }
                }
            }
            if (targetArr.empty())
            {
                return QPointF(0, 0);
            }
            Math::Point2f curEnd = targetArr.front();
            for (auto& p : targetArr)
            {
                if (flag)
                {
                    if (p.y() < curEnd.y())
                    {
                        curEnd = p;
                    }
                }
                else
                {
                    if (p.y() > curEnd.y())
                    {
                        curEnd = p;
                    }
                }
            }
            targetArr = filterProfile(targetArr);
            int begIndex = -1;
            int endIndex = -1;
            for (int i = 0; i < targetArr.size(); ++i)
            {
                auto& p = targetArr[i];
                if (p == curLeft)
                {
                    begIndex = i;
                }
                if (p == curEnd)
                {
                    endIndex = i;
                }
            }
            if (begIndex == -1 || endIndex == -1)
            {
                return QPointF(0, 0);
            }
            std::tie(begIndex, endIndex) = std::minmax(begIndex, endIndex);
            Math::Point2f& a = targetArr[begIndex];
            Math::Point2f& b = targetArr[endIndex];
            Math::Point2f maxPt = a;
            float maxDis = 0;
            for (int i = begIndex; i <= endIndex; ++i)
            {
                auto& p = targetArr[i];
                float val = distancePointToLineMath(p, a, b);
                if (val > maxDis)
                {
                    maxDis = val;
                    maxPt = p;
                }
            }
            return QPointF(maxPt.x(), maxPt.y());
        }

        QPointF MeasureToolImpl::findInnerCorner2(
            Core::KDTreePtr kd, const QPointF& boundRigh,
            float xBeg, float xEnd, float yBeg, float yEnd,
            const Geom::Line2f& midLine,
            const Math::Point2f& midP, bool flag) const
        {
            std::vector<Math::Point2f> radRes;
            kd->boxSearch(xBeg, xEnd, yBeg, yEnd, radRes);
            if (radRes.empty())
            {
                return QPointF(0, 0);
            }
            Math::Point2f curLeft(boundRigh.x(), boundRigh.y());
            for (auto& p : radRes)
            {
                if (p.x() < curLeft.x())
                {
                    curLeft = p;
                }
            }
            Math::Point2f lA(midP.x() - 0.5, midLine.y_at_x(midP.x() - 0.5));
            Math::Point2f lB(midP.x() + 0.5, midLine.y_at_x(midP.x() + 0.5));
            auto dirAB = lB - lA;
            dirAB = rotateMathVector(dirAB, 90, false);
            auto p1 = midP + dirAB * 0.5;
            auto p2 = midP + dirAB * 1.0;
            Math::Point2f minPoint = radRes.front();
            float minDis = distancePointToLineMath(minPoint, p1, p2);
            for (auto& p : radRes)
            {
                if (!mathPointInLine(p1, p2, p))
                {
                    continue;
                }
                float val = distancePointToLineMath(p, p1, p2);
                if (val < minDis)
                {
                    minDis = val;
                    minPoint = p;
                }
            }
            return QPointF(minPoint.x(), minPoint.y());
        }

        Math::Point2f MeasureToolImpl::searchMinDisPoint(const std::vector<Math::Point2f> arr, const Math::Point2f& p1, const Math::Point2f& p2) const
        {
            Math::Point2f minPoint = arr.front();
            float minDis = distancePointToLineMath(minPoint, p1, p2);
            for (auto& p : arr)
            {
                if (!mathPointInLine(p1, p2, p))
                {
                    continue;
                }
                float val = distancePointToLineMath(p, p1, p2);
                if (val < minDis)
                {
                    minDis = val;
                    minPoint = p;
                }
            }
            return minPoint;
        }

        MeasureTool::MeasureTool(const ProductParams& prdParam, QGraphicsItem* parent)
            :QGraphicsItem(parent)
        {
            OI_I(MeasureTool)->init(prdParam);
        }

        MeasureTool::~MeasureTool()
        {
            OI_E(MeasureTool)->exit();
        }

        QRectF MeasureTool::boundingRect() const
        {
            OI_C(MeasureTool);
            if (scene())
            {
                return scene()->sceneRect().adjusted(10000, 10000, -10000, -10000);
            }

            QPolygonF poly;
            poly.append(d->widthTop);
            poly.append(d->endPointWidthTop);
            poly.append(d->endPointWidthBot);
            poly.append(d->widthBot);
            return poly.boundingRect();
        }

        void MeasureTool::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
        {
            OI_F(MeasureTool);

            QTransform transform = painter->transform();
            painter->save();

            painter->scale(1 / transform.m11(), 1 / transform.m22());

            int fontSize = 30;
            auto font = painter->font();
            font.setPointSize(fontSize);
            painter->setFont(font);

            QPen solidPen(Qt::darkRed, 1, Qt::SolidLine);
            float circleRadius = 0.1 * transform.m22();

#pragma region 绘制端点宽
            QPointF p1((d->dspEndPointWidthTop.x() + 0.2) * transform.m11(), d->dspEndPointWidthTop.y() * transform.m22());
            QPointF p2((d->dspEndPointWidthTop.x() + 2) * transform.m11(), d->dspEndPointWidthTop.y() * transform.m22());
            painter->drawLine(p1, p2);
            p1.setY(d->dspEndPointWidthBot.y() * transform.m22());
            p2.setY(d->dspEndPointWidthBot.y() * transform.m22());
            painter->drawLine(p1, p2);
            QPointF p3 = (p1 + p2) / 2;
            QPointF p4(p3.x(), (d->dspEndPointWidthBot.y() - 1) * transform.m22());
            painter->drawLine(p4, p3);
            painter->fillPath(arrowPath(p4, p3, 0, 2, 10), Qt::darkRed);
            p3.setY(d->dspEndPointWidthTop.y() * transform.m22());
            p4.setY((d->dspEndPointWidthTop.y() + 1) * transform.m22());
            painter->drawLine(p3, p4);
            painter->fillPath(arrowPath(p3, p4, 0, 1, 10), Qt::darkRed);
            float msrVal = std::abs(d->dspEndPointWidthTop.y() - d->dspEndPointWidthBot.y());
            QString text = "端点宽: " + QString::number(msrVal, 'f', 2);
            float stdVal = std::abs(d->endPointWidthTop.y() - d->endPointWidthBot.y());
            float maxVal = stdVal + d->config["端点宽Max"].toFloat();
            float minVal = stdVal + d->config["端点宽Min"].toFloat();
            drawCurValue(painter, p4, text, maxVal, minVal);
            drawStdValue(painter, p4, text, d->config["端点宽Max"].toFloat(), d->config["端点宽Min"].toFloat());
            painter->fillPath(circlePath(d->dspEndPointWidthTop, circleRadius, transform.m11(), transform.m22()), Qt::red);
            painter->fillPath(circlePath(d->dspEndPointWidthBot, circleRadius, transform.m11(), transform.m22()), Qt::red);
#pragma endregion

#pragma region 绘制总宽
            float leftX = qMin((qMin(d->dspWidthTop.x(), d->dspWidthBot.x())), d->dspFullHighLef.x());
            leftX -= 3;
            p1.setX(leftX * transform.m11());
            p1.setY(d->dspWidthTop.y() * transform.m22());
            p2 = QPointF(d->dspWidthTop.x() * transform.m11(), d->dspWidthTop.y() * transform.m22());
            //painter->drawLine(p1, p2);
            p1.setY(d->dspWidthBot.y() * transform.m22());
            p2 = QPointF(d->dspWidthBot.x() * transform.m11(), d->dspWidthBot.y() * transform.m22());
            //painter->drawLine(p1, p2);
            p3 = QPointF((leftX + 0.2)* transform.m11(), d->dspWidthTop.y() * transform.m22());
            p4 = QPointF((leftX + 0.2)* transform.m11(), d->dspWidthBot.y() * transform.m22());
            //painter->drawLine(p3, p4);
            //painter->fillPath(arrowPath(p3, p4, 0, 0, 10), Qt::darkRed);
            //painter->fillPath(arrowPath(p4, p3, 0, 0, 10), Qt::darkRed);
            QPointF midP = (p3 + p4) / 2;
            msrVal = std::abs(d->dspWidthTop.y() - d->dspWidthBot.y());
            text = QString::number(msrVal, 'f', 2);
            //painter->drawText(midP, text);
            //drawStdValue(painter, midP, text, std::abs(d->widthTop.y() - d->widthBot.y()));
#pragma endregion

#pragma region 绘制总高
            float botY = qMin(qMin(d->dspFullHighLef.y(), d->dspFullHighLef.y()), d->dspWidthBot.y());
            botY -= 5;
            p1 = QPointF(d->dspFullHighLef.x()* transform.m11(), botY * transform.m22());
            p2 = QPointF(d->dspFullHighRig.x()* transform.m11(), botY * transform.m22());
            painter->drawLine(p1, QPointF(d->dspFullHighLef.x() * transform.m11(), d->dspFullHighLef.y() * transform.m22()));
            painter->drawLine(p2, QPointF(d->dspFullHighRig.x() * transform.m11(), d->dspFullHighRig.y() * transform.m22()));
            p3 = QPointF(p1.x(), (botY + 0.1) * transform.m22());
            p4 = QPointF(p2.x(), (botY + 0.1) * transform.m22());
            painter->drawLine(p3, p4);
            painter->fillPath(arrowPath(p3, p4, 0, 0, 10), Qt::darkRed);
            painter->fillPath(arrowPath(p4, p3, 0, 0, 10), Qt::darkRed);
            midP = QPointF((p3.x() + p4.x()) / 5, (p3.y() + p4.y()) / 2);
            msrVal = std::abs(d->dspFullHighRig.x() - d->dspFullHighLef.x());
            text = "总高: " + QString::number(msrVal, 'f', 2);
            stdVal = std::abs(d->fullHighRig.x() - d->fullHighLef.x());
            maxVal = stdVal + d->config["总高Max"].toFloat();
            minVal = stdVal + d->config["总高Min"].toFloat();
            drawCurValue(painter, midP, text, maxVal, minVal);
            drawStdValue(painter, midP, text, d->config["总高Max"].toFloat(), d->config["总高Min"].toFloat());
            painter->fillPath(circlePath(d->dspFullHighLef, circleRadius, transform.m11(), transform.m22()), Qt::red);
            painter->fillPath(circlePath(d->dspFullHighRig, circleRadius, transform.m11(), transform.m22()), Qt::red);
#pragma endregion

#pragma region 绘制腰宽
            p1.setX(d->dspWaistTop.x() * transform.m11());
            p1.setY(d->dspWaistBot.y() * transform.m22());
            p2.setX(d->dspWaistTop.x() * transform.m11());
            p2.setY(d->dspWaistTop.y() * transform.m22());
            painter->drawLine(p1, p2);
            painter->fillPath(arrowPath(p1, p2, 0, 0, 10), Qt::darkRed);
            midP = (p1 + p2) / 2.0;
            msrVal = std::abs(d->dspWaistTop.y() - d->dspWaistBot.y());
            text = "腰宽: " + QString::number(msrVal, 'f', 2);
            stdVal = std::abs(d->waistTop.y() - d->waistBot.y());
            maxVal = stdVal + d->config["腰宽Max"].toFloat();
            minVal = stdVal + d->config["腰宽Min"].toFloat();
            drawCurValue(painter, midP, text, maxVal, minVal);
            drawStdValue(painter, midP, text, d->config["腰宽Max"].toFloat(), d->config["腰宽Min"].toFloat());
            painter->fillPath(circlePath(p1, circleRadius, 1, 1), Qt::red);
            painter->fillPath(circlePath(p2, circleRadius, 1, 1), Qt::red);
#pragma endregion

            //if (d->tp != DT_TriBoard)
            //{

#pragma region 绘制中厚
                //if (d->dspMidBot != d->dspMidTop && std::abs(d->dspMidTop.x() - d->dspWaistTop.x()) > 2)
                //{
                //    p1.setX(d->dspMidTop.x() * transform.m11());
                //    p1.setY(d->dspMidTop.y() * transform.m22());
                //    p2.setX(d->dspMidBot.x() * transform.m11());
                //    p2.setY(d->dspMidBot.y() * transform.m22());
                //    painter->drawLine(p1, p2);
                //    painter->fillPath(arrowPath(p1, p2, 0, 0, 10), Qt::darkRed);
                //    midP = (p1 + p2) / 2.0;
                //    msrVal = std::abs(d->dspMidBot.y() - d->dspMidTop.y());
                //    text = QString::number(msrVal, 'f', 2);
                //    painter->drawText(midP, text);
                //    drawStdValue(painter, midP, text, std::abs(d->midBot.y() - d->midTop.y()));
                //}
#pragma endregion

#pragma region 绘制腰高
                if (std::abs(d->dspWaistHighLef.x() - d->dspFullHighLef.x()) > 0.5)
                {
                    botY = d->dspWaistHighLef.y() < d->dspWaistHighRig.y() ? d->dspWaistHighLef.y() : d->dspWaistHighRig.y();
                    botY -= 2;
                    p1.setX(d->dspWaistHighLef.x() * transform.m11());
                    p1.setY(d->dspWaistHighLef.y() * transform.m22());
                    p2.setX(d->dspWaistHighRig.x() * transform.m11());
                    p2.setY(d->dspWaistHighRig.y() * transform.m22());
                    p3.setX(p1.x());
                    p3.setY(botY * transform.m22());
                    p4.setX(p2.x());
                    p4.setY(botY * transform.m22());
                    painter->drawLine(p1, p3);
                    painter->drawLine(p2, p4);
                    painter->drawLine(p3, p4);
                    painter->fillPath(arrowPath(p3, p4, 0, 0, 10), Qt::darkRed);
                    midP = QPointF((p3.x() + p4.x()) / 5, (p3.y() + p4.y()) / 2);
                    msrVal = std::abs(d->dspWaistHighLef.x() - d->dspWaistHighRig.x());
                    text = "腰高: " + QString::number(msrVal, 'f', 2);
                    stdVal = std::abs(d->waistHighLef.x() - d->waistHighRig.x());
                    maxVal = stdVal + d->config["腰高Max"].toFloat();
                    minVal = stdVal + d->config["腰高Min"].toFloat();
                    drawCurValue(painter, midP, text, maxVal, minVal);
                    drawStdValue(painter, midP, text, d->config["腰高Max"].toFloat(), d->config["腰高Min"].toFloat());
                    painter->fillPath(circlePath(d->dspWaistHighLef, circleRadius, transform.m11(), transform.m22()), Qt::red);
                    painter->fillPath(circlePath(d->dspWaistHighRig, circleRadius, transform.m11(), transform.m22()), Qt::red);
                }
#pragma endregion

#pragma region 绘制底部宽
                QPointF dir1 = d->dspBottomWidthTop - d->dspBottomWidthBot;
                QPointF dir2 = d->dspBottomWidthBot - d->dspBottomWidthTop;
                QPointF dir3 = rotateVector(dir1, 90, false);
                QPointF dir4 = rotateVector(dir2, 90, true);
                p1 = d->dspBottomWidthBot + 2 * dir3;
                p2 = d->dspBottomWidthTop + 2 * dir4;
                QPointF tempBot(d->dspBottomWidthBot.x() * transform.m11(), d->dspBottomWidthBot.y() * transform.m22());
                QPointF tempTop(d->dspBottomWidthTop.x() * transform.m11(), d->dspBottomWidthTop.y() * transform.m22());
                p1.setX(p1.x() * transform.m11());
                p1.setY(p1.y() * transform.m22());
                p2.setX(p2.x() * transform.m11());
                p2.setY(p2.y() * transform.m22());
                painter->drawLine(tempBot, p1);
                painter->drawLine(tempTop, p2);
                painter->drawLine(p1, p2);
                painter->fillPath(arrowPath(p1, p2, 0, 0, 10), Qt::darkRed);
                midP = (p1 + p2) / 2;
                msrVal = pointDis(d->dspBottomWidthBot, d->dspBottomWidthTop);
                text = "底部宽: " + QString::number(msrVal, 'f', 2);
                midP.rx() -= fontSize * text.length() * 2.1;
                stdVal = pointDis(d->bottomWidthBot, d->bottomWidthTop);
                maxVal = stdVal + d->config["底部宽Max"].toFloat();
                minVal = stdVal + d->config["底部宽Min"].toFloat();
                drawCurValue(painter, midP, text, maxVal, minVal);
                drawStdValue(painter, midP, text, d->config["底部宽Max"].toFloat(), d->config["底部宽Min"].toFloat());
                painter->fillPath(circlePath(d->dspBottomWidthTop, circleRadius, transform.m11(), transform.m22()), Qt::red);
                painter->fillPath(circlePath(d->dspBottomWidthBot, circleRadius, transform.m11(), transform.m22()), Qt::red);
#pragma endregion

#pragma region 绘制截面积
                QPointF areaPos = QPointF(0, d->dspWidthTop.y());
                areaPos.setX(areaPos.x() * transform.m11());
                areaPos.setY(areaPos.y() * transform.m22());
                text = QString("截面积: %1").arg(d->currArea, 0, 'f', 2);
                stdVal = d->config["截面积"].toFloat();
                maxVal = stdVal + d->config["截面积Max(%)"].toFloat() * stdVal / 100;
                minVal = stdVal + d->config["截面积Min(%)"].toFloat() * stdVal / 100;
                drawCurValue(painter, areaPos, text, maxVal, minVal);
                drawStdValue(painter, areaPos, text, d->config["截面积Max(%)"].toFloat(), d->config["截面积Min(%)"].toFloat());
#pragma endregion

            //}

            if (acceptHoverEvents())
            {
                painter->drawText(QPointF(d->_hoverPos.x() * transform.m11(), d->_hoverPos.y() * transform.m22()), QString("(%1, %2)").arg(d->_hoverPos.x()).arg(d->_hoverPos.y()));
            }

            painter->restore();
        }

        void MeasureTool::chaseMouse(bool chasing)
        {
            OI_F(MeasureTool);
            setAcceptHoverEvents(chasing);
        }

        bool MeasureTool::isChaseMouse()
        {
            OI_F(MeasureTool);
            return acceptHoverEvents();
        }

        void MeasureTool::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
        {
            OI_F(MeasureTool);

            d->_hoverPos = event->pos();
        }

        std::vector<QPointF> MeasureTool::measure(const OiProfile& profile)
        {
            OI_F(MeasureTool);
            return d->measure(profile);
        }

        void MeasureTool::setResult(const std::vector<QPointF>& msrRes)
        {
            OI_F(MeasureTool);
            if (msrRes.size() < 16)
            {
                OiWarning() << "Measured data is invalid: " << msrRes.size();
                return;
            }

            d->dspEndPointWidthTop = msrRes[0];
            d->dspEndPointWidthBot = msrRes[1];
            d->dspWaistTop = msrRes[2];
            d->dspWaistBot = msrRes[3];
            d->dspWidthTop = msrRes[4];
            d->dspWidthBot = msrRes[5];
            d->dspWaistHighLef = msrRes[6];
            d->dspWaistHighRig = msrRes[7];
            d->dspFullHighLef = msrRes[8];
            d->dspFullHighRig = msrRes[9];
            d->dspMidBot = msrRes[10];
            d->dspMidTop = msrRes[11];
            d->dspBottomWidthTop = msrRes[12];
            d->dspBottomWidthBot = msrRes[13];
            d->currArea = msrRes[15].y();
        }

        void MeasureTool::resetDisplayData()
        {
            OI_F(MeasureTool);
            d->dspEndPointWidthTop = d->endPointWidthTop;
            d->dspEndPointWidthBot = d->endPointWidthBot;
            d->dspWaistTop = d->waistTop;
            d->dspWaistBot = d->waistBot;
            d->dspWidthTop = d->widthTop;
            d->dspWidthBot = d->widthBot;
            d->dspMidBot = d->midBot;
            d->dspMidTop = d->midTop;
            d->dspWaistHighLef = d->waistHighLef;
            d->dspWaistHighRig = d->waistHighRig;
            d->dspFullHighLef = d->fullHighLef;
            d->dspFullHighRig = d->fullHighRig;
            d->dspBottomWidthTop = d->bottomWidthTop;
            d->dspBottomWidthBot = d->bottomWidthBot;
        }

        std::pair<std::vector<double>, std::vector<double>> MeasureTool::calcResult()
        {
            OI_F(MeasureTool);
            std::pair<std::vector<double>, std::vector<double>> res;
            auto& calcArr = res.first;
            auto& templArr = res.second;
            calcArr.resize(9);
            templArr.resize(9);

            calcArr[1] = std::abs(d->dspEndPointWidthTop.y() - d->dspEndPointWidthBot.y());
            calcArr[2] = std::abs(d->dspWaistTop.y() - d->dspWaistBot.y());
            calcArr[3] = std::abs(d->dspWidthTop.y() - d->dspWidthBot.y());
            calcArr[4] = std::abs(d->dspWaistHighLef.x() - d->dspWaistHighRig.x());
            calcArr[5] = std::abs(d->dspFullHighRig.x() - d->dspFullHighLef.x());
            calcArr[6] = std::abs(d->dspMidBot.y() - d->dspMidTop.y());
            calcArr[7] = pointDis(d->dspBottomWidthBot, d->dspBottomWidthTop);
            calcArr[8] = d->currArea;

            templArr[1] = std::abs(d->endPointWidthTop.y() - d->endPointWidthBot.y());
            templArr[2] = std::abs(d->waistTop.y() - d->waistBot.y());
            templArr[3] = std::abs(d->widthTop.y() - d->widthBot.y());
            templArr[4] = std::abs(d->waistHighLef.x() - d->waistHighRig.x());
            templArr[5] = std::abs(d->fullHighRig.x() - d->fullHighLef.x());
            templArr[6] = std::abs(d->midBot.y() - d->midTop.y());
            templArr[7] = pointDis(d->bottomWidthBot, d->bottomWidthTop);
            templArr[8] = d->config["截面积"].toFloat(); 

            int finalResult = WorkResult::RC_Success;
            if (calcArr[1] > templArr[1] + d->config["端点宽Max"].toFloat()    || calcArr[1] < templArr[1] + d->config["端点宽Min"].toFloat() ||
                calcArr[2] > templArr[2] + d->config["腰宽Max"].toFloat()      || calcArr[2] < templArr[2] + d->config["腰宽Min"].toFloat() ||
                calcArr[4] > templArr[4] + d->config["腰高Max"].toFloat()      || calcArr[4] < templArr[4] + d->config["腰高Min"].toFloat() ||
                calcArr[5] > templArr[5] + d->config["总高Max"].toFloat()      || calcArr[5] < templArr[5] + d->config["总高Min"].toFloat() ||
                calcArr[7] > templArr[7] + d->config["底部宽Max"].toFloat()    || calcArr[7] < templArr[7] + d->config["底部宽Min"].toFloat() ||
                calcArr[8] > templArr[8] + d->config["截面积Max(%)"].toFloat() * templArr[8] / 100 || calcArr[8] < templArr[8] + d->config["截面积Min(%)"].toFloat() * templArr[8] / 100)
            {
                finalResult = WorkResult::RC_Error;
            }

            calcArr[0] = finalResult;
            templArr[0] = finalResult;

            //if (d->tp == DT_TriBoard)
            //{
            //    calcArr[3] = calcArr[4];
            //    templArr[3] = templArr[4];
            //    calcArr[5] = calcArr[1];
            //    templArr[5] = templArr[1];
            //}

            return res;
        }

        void MeasureTool::drawCurValue(QPainter* painter, const QPointF& curPt, const QString& text, float maxVal, float minVal)
        {
            OI_F(MeasureTool);
            auto& ft = painter->font();
            int ftSz = ft.pointSize();
            QFontMetrics ftMs(ft);
            auto txtRect = ftMs.tightBoundingRect(text);
            txtRect.translate(curPt.x(), curPt.y());
            QColor resultColor;
            float currValue = 0;
            if (text.contains(":"))
            {
                auto splits = text.split(":");
                if (splits.size() == 2)
                {
                    currValue = splits[1].toFloat();
                }
            }
            else
            {
                currValue = text.toFloat();
            }
            if (currValue <= maxVal && currValue >= minVal)
            {
                resultColor = QColor(Qt::green);
            }
            else
            {
                resultColor = QColor(Qt::red);
            }
            painter->fillRect(txtRect, resultColor);
            painter->drawText(curPt, text);
        }
        
        void MeasureTool::drawStdValue(QPainter* painter, const QPointF& prePt, const QString& preText, float maxVal, float minVal)
        {
            OI_F(MeasureTool);
            auto& ft = painter->font();
            int ftSz = ft.pointSize();
            QFontMetrics ftMs(ft);
            QPointF curPt = prePt;
            curPt.rx() += ftSz * preText.length();
            QString text;
            if (qAbs(maxVal) != qAbs(minVal))
            {
                text = QString("(") + QString::number(minVal, 'f', 2) + QString("~") + QString::number(maxVal, 'f', 2) + QString(")");
            }
            else
            {
                text = QString("(±%1)").arg(maxVal, 0, 'f', 2);
            }
            auto txtRect = ftMs.tightBoundingRect(text);
            txtRect.translate(curPt.x(), curPt.y());
            painter->fillRect(txtRect, Qt::yellow);
            painter->drawText(curPt, text);
        }

    }
}
